#include <QMessageBox>
#include <QFile>

#include "../../mainwindow.h"
#include "../../datamanager/reg3volumedatanode.h"
#include "../../contree/augmentedctree.h"
#include "../../contree/contreeconstructor3d.h"
#include "../../mact/dualgraph.h"
#include "../../mact/dctconstructor.h"
#include "../../mact/mact.h"
#include "../../renderer/rendermanager.h"
#include "../../data/fileplugin.h"
#include "macttoolsplugin.h"
#include "dctreecontrol.h"
#include "matchcontrol.h"
#include "mactdisplay.h"

using namespace PBVP;

MactToolsPlugin::MactToolsPlugin()
{

}

MactToolsPlugin::~MactToolsPlugin()
{
	delete m_pDCTControl;
	delete m_pMatchControl;
}

void MactToolsPlugin::initialize(MainInterface* mainInterface)
{
	ToolPlugin::initialize(mainInterface);

	m_pDataManager = m_pMainInterface->getDataManager();
	m_pRenderManager = m_pMainInterface->getRenderManager();
	m_pFileExtManager = m_pMainInterface->getFileExtensionManager();

	m_pDCTControl = new DCTreeControl(m_pFileExtManager);
	m_pMatchControl = new MatchControl(m_pFileExtManager);

	mactMenu = new QMenu(tr("MACT Tools"), m_pMainInterface->getMainWindow());
	mactMenu->setStatusTip(description());

	dctAction = new QAction(tr("Construct Dual Contour Tree"), mactMenu);
	connect(dctAction, SIGNAL(triggered()), this, SLOT(constructDCTree()));

	mactMatchAction = new QAction(tr("MACT Match Volumes"), mactMenu);
	connect(mactMatchAction, SIGNAL(triggered()), this, SLOT(mactMatch()));

	mactMenu->addAction(dctAction);
	mactMenu->addAction(mactMatchAction);
}

bool MactToolsPlugin::isSupported(DataNode* pNode)
{
#ifdef __GNU__
	if(pNode->data()->getDataSetType() == VOLUME_DATA) return true;
#else
	Reg3DVolumeDataNode *pVolNode;
	pVolNode = dynamic_cast<Reg3DVolumeDataNode *>(pNode);
	if (pVolNode)
	{
		return true;
	}
#endif
	return false;
}

//////////////////////////////////////////////////////////////////////////
// Public Slots
//////////////////////////////////////////////////////////////////////////
void MactToolsPlugin::constructDCTree()
{
	Reg3DVolumeDataNode *pVolNode = NULL;

	DataNode *pNode = m_pDataManager->getCurrentNode();
	if (pNode)
	{
#ifdef __GNU__
		pVolNode = (Reg3DVolumeDataNode *)(pNode);
#else
		pVolNode = dynamic_cast<Reg3DVolumeDataNode *>(pNode);
#endif
	}
	m_pDCTControl->init(pVolNode);

	if (m_pDCTControl->exec())
	{
		QString dctFileName = m_pDCTControl->getOutputPath();
		QFile dctFile(dctFileName);
		if (dctFile.exists())
		{
			if (QMessageBox::question(m_pMainInterface->getMainWindow(), "Warning",
									  QString("The file ").append(dctFileName).append(" exists.\nDo you want to replace it?"),
									  QMessageBox::Ok | QMessageBox::Cancel)
				== QMessageBox::Cancel)
			{
				printf("cancel DCT construction\n");
				return;
			}
		}

		QString volFileName = m_pDCTControl->getVolumeFileName();

		DualGraph* pDCTree = getDCTree(volFileName, dctFileName);

		if(pDCTree != NULL)
		{
			pDCTree->saveFile(dctFileName.toAscii().data());
			pDCTree->print();
			delete pDCTree;
		}
	}
}

void MactToolsPlugin::mactMatch()
{
	QString volFileName1, volFileName2;
	QString dctFileName1, dctFileName2;

	if (m_pMatchControl->exec())
	{
		volFileName1 = m_pMatchControl->getVolFileName1();
		volFileName2 = m_pMatchControl->getVolFileName2();
		dctFileName1 = m_pMatchControl->getDCTFileName1();
		dctFileName2 = m_pMatchControl->getDCTFileName2();

		DualGraph *pDCTree1 = NULL, *pDCTree2 = NULL;
		// DCT files must be precomputed to run the match tool
		pDCTree1 = new DualGraph();
		if(! pDCTree1->loadFile(dctFileName1.toAscii().data()))
		{
			QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
				tr("Cannot open DCT file of volume 1\n Please compute it before running the match tool"));
			delete pDCTree1;
			return;
		}
		pDCTree2 = new DualGraph();
		if (! pDCTree2->loadFile(dctFileName2.toAscii().data()))
		{
			QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
				tr("Cannot open DCT file of volume 2\n Please compute it before running the match tool"));
			delete pDCTree1;
			delete pDCTree2;
			return;
		}
		MultiConTree mact1(pDCTree1);
		MultiConTree mact2(pDCTree2);

		SimScore sim = mact1.match(mact2);
		double weights[4];
		m_pMatchControl->getWeights(weights);
		sim.setWeights(weights[0], weights[1], weights[2], weights[3]);
		// Display top node attributes and match results
		MactDisplay mactDisplay(mact1.toQString(), mact2.toQString(), sim.toQString(),
			m_pMainInterface->getMainWindow());
		mactDisplay.exec();

		sim.print();
		printf("matching score = %f\n", sim.average());
	}
}

/********************************************************************************************/
DualGraph* MactToolsPlugin::getDCTree(const QString &volFileName, const QString& dctFileName)
{
	if (volFileName.isEmpty())
	{
		QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
							  tr("No Volume File is Specified"));
		return NULL;
	}
	// load the volume and property files into a NEW Reg3D Volume Data
	FilePluginInterface *pFilePlugin = m_pFileExtManager->getSupportedPlugin(volFileName);
	if (!pFilePlugin)
	{
		QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
							  QString("Cannot Open Volume File: ").append(volFileName));
		return NULL;
	}
	DataInterface *pData = pFilePlugin->loadFile(volFileName);
	Reg3DVolumeData *pVolData = NULL;
#ifdef __GNU__
	if(pData->getDataSetType() == VOLUME_DATA) pVolData = (Reg3DVolumeData *)(pData);
#else
	pVolData = dynamic_cast<Reg3DVolumeData *>(pData);
#endif
	if (!pVolData)
	{
		// Not a valid Reg3D volume data
		delete pData;
		QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
							  QString("Failed to Load File: ").append(volFileName));
		return NULL;
	}
	unsigned int volDim[3];
	pVolData->getDimension(volDim);

	int nProp = m_pDCTControl->numOfProperties();
	for (int i = 0; i < nProp; i++)
	{
		QString propFileName = m_pDCTControl->getPropertyFileName(i);
		pFilePlugin = m_pFileExtManager->getSupportedPlugin(propFileName);
		if (!pFilePlugin)
		{
			QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
								  QString("Cannot Open Property File: ").append(propFileName));
			delete pVolData;
			return NULL;
		}
		DataInterface *pData = pFilePlugin->loadFile(propFileName);
		Reg3DVolumeData *pPropData = NULL;
#ifdef __GNU__
		if(pData->getDataSetType() == VOLUME_DATA) pPropData = (Reg3DVolumeData *)(pData);
#else
		pPropData = dynamic_cast<Reg3DVolumeData *>(pData);
#endif
		if (!pPropData)
		{
			delete pData;
			QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
								  QString("Failed to Load File: ").append(propFileName));
			delete pVolData;
			return NULL;
		}
		unsigned int propDim[3];
		pPropData->getDimension(propDim);
		if (volDim[0] != propDim[0] || volDim[1] != propDim[1] || volDim[2] != propDim[2])
		{
			QMessageBox::critical(m_pMainInterface->getMainWindow(), "Error",
								  QString("The Dimensions of Volume Don't Match with the Properties"));
			delete pPropData;
			delete pVolData;
			return NULL;
		}
		// add property to the volume data
		pVolData->mergeData(pPropData);
		delete pPropData;
	}
	// pVolNode = new Reg3DVolumeDataNode(pVolData, m_pMainInterface);
	// pVolNode->setName(volFileName);
	// m_pRenderManager->setDefaultRenderer(pVolNode);
	// m_pDataManager->addDataNode(pVolNode);

	// load or construct contour tree
	AugmentedContourTree *pACTree = new AugmentedContourTree;
	QString cTreeName(volFileName);
	cTreeName.append(".ctr");
	if (!pACTree->loadFile(cTreeName.toAscii().data()))
	{
		if (QMessageBox::warning(m_pMainInterface->getMainWindow(), "Warning",
								 "Recomputing Contour Tree is slow and resource intensive.\n Are you sure to start?",
								 QMessageBox::Ok|QMessageBox::Cancel) != QMessageBox::Ok)
		{
			delete pACTree;
			return NULL;
		}
		ConTreeConstructorReg3D *construcCT = new ConTreeConstructorReg3D(pVolData);
		delete pACTree;
		pACTree = construcCT->getAugmentedContourTree();
		pACTree->saveFile(cTreeName.toAscii().data());
		delete construcCT;
	}

	// construct dual contour tree
	DCTConstructor *pDCTContructor = new DCTConstructor(pVolData, pACTree);
	DualGraph *pDCTree = pDCTContructor->buildDualGraph(m_pDCTControl->getRangeMin(),
														m_pDCTControl->getRangeMax(), m_pDCTControl->numOfLevels());
	delete pDCTContructor;
	delete pACTree;
	delete pVolData;

	return pDCTree;
}
Q_EXPORT_PLUGIN2(macttools, MactToolsPlugin)
